class ContestsController < ApplicationController
  include Authorizer
  include Configurator
  layout 'standart'
  before_filter :login_required ,:except => [:show, :index, :board]

  def index
    @contests = Contest.paginate :page => params[:page]
  end

  def edit
    @contest = Contest.find_by_contest_id(params[:id])
  end
  
  def show
    @contest = Contest.find_by_contest_id(params[:id])
    config = load_config
    @problems = Problem.find(:all, :conditions => {:contest_id => @contest.contest_id})
    h = {}
    h[:problem_id] = @problems.map{|p| p.problem_id}

    unless params[:user].nil?
      @user = User.find_by_id(params[:user])
      h[:user_id] = params[:user] 
    end

    order_string = ""
    @runs = ""
    @order = params[:order] 
    if @order && config['runs_can_be_sorted_by'].include?(params[:order])
      order_string = config['runs_can_be_sorted_by'][@order]
      order_string += " DESC " if params[:order] == config['runs_can_be_sorted_by']['submit_time']
    end
    if order_string.length > 0
      @runs = Run.paginate(:page => params[:page], :conditions => h,:include => ['problem','potrebitel'],
        :order => order_string,:per_page => config['number_per_page'])
    else
      @runs = Run.paginate(:page => params[:page], :conditions => h,:include => ['problem','potrebitel'],
        :per_page => config['number_per_page'])
    end
  end

  def new
    @contest = Contest.new
  end

  def create
    @contest = Contest.new(params[:contest])
    time = Time.mktime(params[:year].to_i,params[:month].to_i,params[:day].to_i,
                                        params[:hour].to_i,params[:minute].to_i)
    # TODO: Това трябва да може да стане по-лесно
    if time.month != params[:month].to_i
      @year = params[:year]
      @month = params[:month]
      @day = params[:day]
      @hour = params[:hour]
      @minute = params[:minute]
      @duration = params[:duration]
      @type = params[:type].to_i
      flash[:error] = "Incorect date specified!"
      render :action => :new
    else
      @contest.start_time = time
      @contest.show_sources = 0
      if numeric?(params[:duration])
        @contest.duration = params[:duration].to_i*params[:type].to_i
      else
        @contest.duration = params[:duration]
      end 
      if @contest.save
        flash[:notice] = "Contest successfully edited!"
        redirect_to :controller => :spoj1_admin,:action => :index
      else 
        flash[:error] = "Could not create the contest!"
        @year = params[:year]
        @month = params[:month]
        @day = params[:day]
        @hour = params[:hour]
        @minute = params[:minute]
        @duration = params[:duration]
        @type = params[:type].to_i
        render :action => :new
      end 
    end
  end
  
  def update
    time = Time.mktime(params[:year].to_i,params[:month].to_i,params[:day].to_i,
                                        params[:hour].to_i,params[:minute].to_i)
    if time.month != params[:month].to_i
      flash[:error] = "Incorect date specified!"
      render :action => :edit
    else
      @contest = Contest.find_by_contest_id(params[:id])
      @contest.start_time = time
      @contest.duration = params[:contest][:duration]
      @contest.name = params[:contest][:name]
      @contest.about = params[:contest][:about]
      if @contest.save
        flash[:notice] = "Contest successfully edited!"
        redirect_to :controller => :spoj1_admin,:action => :index
      else 
        flash[:error] = "Could not edit the contest!"
        render :action => :edit
      end 
    end
  end



  def board
    # Модул, който показва класирането на потребителите за съответното състезание
    # От името на екипа на Споджи, искам да се извиня за този метод.
    # Функцията е сложна, затова методът е сложен. Някой ден, ако има време,
    # ще се върнем, ще се посмеем за добрите стари времена и ще го напишем по-кратко.
    def ok_status_string attempts,time
        # Когато задачата е решена, показва се времето на успешното решение,
        # знак "плюс", както и броят на неуспешните опити (дори и да е 0)
        "#{time}+(#{attempts})"
    end

    def wrong_status_string attempts
        # Когато задачата не е решена, показва се минус и броят на грешните опити.
        # Ако опитите са 0, не се показва нищо
        if attempts > 0
            "-(#{attempts})"
        else
            ""
        end
    end

    @contest = Contest.find_by_contest_id(params[:id])
    config = load_config
    @problems = Problem.find(:all, :conditions => {:contest_id => @contest.contest_id})
    # Това са всички потребители, които имат поне един опит за решение в състезанието.
    @user_ids = Run.find(:all,:select => "DISTINCT user_id",:conditions => {:problem_id => @problems}).map{|u| u.user_id}
    @users = User.find(:all,:conditions => {:id => @user_ids})
    p "USER IDS #{@user_ids}"
    p "USERS #{@users}"
    p "PROBLEMS #{@problems}"

    # За всеки потребител, за всяка задача, виж какъв е резултатът
    @results = {}
    @users.each do |u|
      # all е броят решени задачи
      # attempts е броят изпратени решения(верни + грешни)
      # time е времето на състезателя(реално това са наказателни точки)
      # (сума от времената на верните решения + 20 мин. * броя на неверните)
      # Забележка: в наказателните точки невярно решение се взема само ако
      # задачата все пак е решена.
      # Т.е. потребител, решил задача 1 на 20-тата минута, от първи път
      # и опитал се да реши задача 2 общо 5 неуспешни пъти, ще има само 20 минути наказателно време.
      @results[u.login] = {:all => 0,:attempts => 0,:time => 0}
      @problems.each do |p|
        # Това е първото успешно решение на задачата
        ok_run = Run.find(:first,:order => :submit_time,
            :conditions =>["user_id = ? AND problem_id = ? AND status = ?",
            u,p,config['status']['ok']])
        if ok_run
          # Задачата е решена
          success_time = ((ok_run.submit_time - @contest.start_time)/60).to_i
          # Наказателните точки се формират от всички неуспешни решения
          # пратени преди първото успешно.
          conditions = {
            :user_id => u,
            :problem_id => p,
            :status => ["status NOT = ?", config['status']['ok']],
            :submit_time => ["submit_time <= ? AND submit_time >= ?", ok_run.submit_time, @contest.start_time]
          }
          
          unsuccessfull = Run.find(:all,:conditions => conditions)
          
          @results[u.login][p.problem_id] = ok_status_string(unsuccessfull.size,success_time)
          @results[u.login][:all] += 1
          @results[u.login][:attempts] += unsuccessfull.size+1
          @results[u.login][:time] += success_time + unsuccessfull.size*config['wrong_submit_penalty']
        else
          # Задачата не е решена
          unsuccessful = Run.find(:all,:conditions => {:user_id => u,:problem_id => p,:status => wrong_status})
          size = (unsuccessful.nil? ? 0 : unsuccessful.size)
          @results[u.login][p.problem_id] = wrong_status_string(size)
          @results[u.login][:attempts] += size
        end
      end
    end

    h = {}
    h[:problem_id] = @problems.map{|p| p.problem_id}
  end


end
